/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年8月7日
 */
package com.massyframework.beanskin.runtime.framework.installing.fragment;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.Objects;
import java.util.function.Consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 模块Jar文件监视器
 * @author huangkh
 *
 */
final class ModuleJarFileWatcher implements Runnable {

	public Logger logger = LoggerFactory.getLogger(ModuleJarFileWatcher.class);
	
	private Path directory;
	private Consumer<WatchEvent<?>> handler;
	
	/**
	 * 
	 */
	public ModuleJarFileWatcher(Path directory, Consumer<WatchEvent<?>> handler) {
		this.directory = Objects.requireNonNull(directory, "\"directory\" cannot be null.");
		this.handler = Objects.requireNonNull(handler, "\"handler\" cannot be null.");
		
		if (!Files.exists(this.directory)) {
			try {
				Files.createDirectories(this.directory);
			} catch (IOException e) {
				if (logger.isErrorEnabled()) {
					logger.error("create directory failed:".concat(this.directory.toFile().getPath()), e);
				}
			}
		}
	}

	@Override
	public void run() {
		WatchService service = null;
		try {
	        service = FileSystems.getDefault().newWatchService();
	    } catch (Exception e) {
	       if (logger.isErrorEnabled()) {
	    	   logger.error("watch directory failed:" +  this.directory.toFile().getPath() , e);
	       }
	    }
		
		try {
	        //获取文件目录下的Path对象注册到 watchService中。
	        //监听的事件类型，有创建，删除，以及修改
	        this.directory
	                .register(service, 
	                		StandardWatchEventKinds.ENTRY_CREATE, 
	                		StandardWatchEventKinds.ENTRY_DELETE,
	                        StandardWatchEventKinds.ENTRY_MODIFY);
	    } catch (IOException e) {
	        if (logger.isErrorEnabled()) {
	        	logger.error(e.getMessage(), e);
	        }
	    }
		
		try {
			while (!Thread.currentThread().isInterrupted()){
				WatchKey key = null;
		        try {
		            //获取可用key.没有可用的就wait
		            key = service.take();
		        } catch (InterruptedException e) {
		            continue;
		        }
		        for (WatchEvent<?> event : key.pollEvents()) {
		        	try {
		        		this.handler.accept(event);
		        	}catch(Throwable ex) {
		        		if (logger.isWarnEnabled()) {
		        			logger.warn(ex.getMessage(), ex);
		        		}
		        	}
		        }
		        
		        //重置，这一步很重要，否则当前的key就不再会获取将来发生的事件
		        boolean valid = key.reset();
		        //失效状态，退出监听
		        if (!valid) {
		            break;
		        }
			}
		}catch(Exception e) {
			if (logger.isErrorEnabled()) {
				logger.error(e.getMessage(), e);
			}
		}
	}
}
